const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const middleware = require('webpack-dev-middleware');
const hotMiddleWare = require('webpack-hot-middleware');
const WebpackBar = require('webpackbar');
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
const compression = require('compression');
const express = require('express');
const address = require('address');
const cors = require('cors');
const os = require('os');
const execa = require('execa');
const getEntriesRouter = require('./webpack/getEntriesRouter.development');
const { matchByPathname } = require('./webpack/matchRoute.development');
const CONFIG = require('./webpack.config.babel');
const { set } = require('./webpack/buildHook');
const { createProxyMiddleware } = require('http-proxy-middleware');
require('colors');

// 获取自定义port
let [, httpPort = 8000] =
    process.argv
        .slice(2)
        .join(' ')
        .match(/-p\s*(\d+)/) || [];
httpPort = Number(httpPort);
const httpsPort = httpPort + 1;

const options = {
    key: fs.readFileSync(path.join(__dirname, './.ssh', 'key.pem')),
    cert: fs.readFileSync(path.join(__dirname, './.ssh', 'cert.pem')),
};

const getIp = () =>
    new Promise((resolve) => {
        resolve(address.ip());
    });

function getFrpcName() {
    const filePath = path.resolve(__dirname, './frpc.ignore.conf');
    let username = '';
    if (fs.existsSync(filePath)) {
        username = fs.readFileSync(filePath).toString().replace(/\s*/g, '');
    }
    return username || os.userInfo().username;
}

function configFrpc() {
    const username = getFrpcName();
    fs.writeFileSync(
        path.join(__dirname, 'bin/frp/frpc.ini'),
        fs
            .readFileSync(path.join(__dirname, 'bin/frp/frpc.template.ini'))
            .toString()
            .replace(/\$\{name\}/g, username)
            .replace(/\$\{port\}/g, httpPort)
    );

    execa('./bin/frp/frpc', ['-c', './bin/frp/frpc.ini'], {
        cwd: process.cwd(),
        stdio: 'inherit',
    });

    return username;
}

getIp().then(() => {
    const username = configFrpc(httpPort);

    console.log(
        [
            '',
            '--------------------------------------------------------',
            '本地服务已启动',
            `PC端使用在【设置 -> 代理】内输入 https://localhost:${httpsPort} 进行代理`,
            `手机端使用在【设置 -> 代理】内输入 ${username} 进行代理`,
            '移动端默认使用的是你的电脑用户名，如果你觉得自己的用户名太繁琐可以使用一下命令修改frpc代理username',
            'echo YOUR_NAME > frpc.ignore.conf',
            '--------------------------------------------------------',
            '',
        ].join('\n').green
    );

    // 添加支持热更新
    const webpackEntry = CONFIG.entry;
    CONFIG.plugins.push(
        new webpack.DefinePlugin({
            'process.env.USER': `"${username}"`,
        })
    );

    Object.keys(webpackEntry).forEach((key) => {
        CONFIG.entry[key] = [
            `webpack-hot-middleware/client?path=https://proxy.wpt.la/@${username}@/__webpack_hmr&reload=true&timeout=1500&noInfo=true`,
        ].concat(webpackEntry[key]);
    });

    const app = express();
    const compiler = webpack(CONFIG);

    compiler.apply(new WebpackBar());

    app.use(cors({ credentials: true }));
    // proxy 设置
    try {
        const proxyConfig = require('./proxy.js');
        for (const key in proxyConfig) {
            const element = proxyConfig[key];
            app.use(key, createProxyMiddleware(element))
            console.log(`代理已启用${key} ---> ${element.target}`)
        }
    } catch (error) {
        console.log('代理启动失败')
        console.log(error)
    }
    app.use((req, res, next) => {
        res.append('Service-Worker-Allowed', '/');
        next();
    });

    app.use(compression()); // 使用gzip压缩，减少文件传输量

    app.all('/webApp/dist/*', function (req, res, next) {
        res.header('cache-control', 'max-age=315360000'); // 打包文件设置强制缓存
        next();
    });

    app.use(
        express.static(path.resolve(__dirname, '../'))
    );

    const instance = middleware(compiler, {
        stats: {
            colors: true,
            modules: false,
        },
        publicPath: '/webApp/dist/',
        // writeToDisk: true,
    });

    app.use(instance);

    app.use(
        hotMiddleWare(compiler, {
            log: false,
            path: '/__webpack_hmr',
        })
    );

    function execOnce(fn) {
        let isExec = false;
        return (...args) => {
            if (isExec) {
                return;
            }
            isExec = true;
            return fn(...args);
        };
    }
    let requesting = false;

    app.get('/compile', (req, res) => {
        const { url } = req.query;
        const { entries, routers } = getEntriesRouter({ srcPath: './src', baseUrl: __dirname });
        const matchedResult = matchByPathname(url, routers);

        requesting = true;

        if (matchedResult) {
            const entryPath = entries[matchedResult.value];
            const entryName = matchedResult.value;

            compiler.apply(new SingleEntryPlugin(process.cwd(), entryPath, entryName));
            instance.invalidate();

            // 确保响应只被执行一次，应该还有其他的修复角度的
            // 避免出现wait until bundle finished
            set(
                execOnce(() => {
                    if (requesting) {
                        res.json({ status: 'done' });
                        res.end();
                        requesting = false;
                    }
                })
            );
        } else {
            res.json({ status: 'not found' });
        }
    });

    app.get('/favicon.ico', (req, res) => {
        res.json({});
    });

    app.get('*', (req, res) => {
        res.sendFile(path.resolve(__dirname, './local.html'));
    });

    http.createServer(app).listen(httpPort, '0.0.0.0');
    https.createServer(options, app).listen(httpsPort, '0.0.0.0');
});
